﻿using System;
using System.Collections.Generic;
using System.IO;

using Microsoft.WowAddonStudio.Lua.Model;

namespace Microsoft.WowAddonStudio.Lua.Loaders
{
    /// <summary>
    /// An abstract <see cref="DocumentationLoader"/> that loads <see cref="Documentation"/>
    /// from files found in a World of Warcraft Table of Contents (TOC) file.
    /// </summary>
    public class TocDocumentationLoader<TStreamProvider> : DocumentationLoader where TStreamProvider : StreamProvider
    {
        private readonly Dictionary<string, FileDocumentationLoader> fileLoaders = new Dictionary<string, FileDocumentationLoader>();
        private readonly TStreamProvider streamProvider;

        public TocDocumentationLoader(TStreamProvider streamProvider)
        {
            if (streamProvider == null)
                throw new ArgumentNullException("streamProvider");

            this.streamProvider = streamProvider;
            this.InitializeLoaders();
        }

        private void InitializeLoaders()
        {
            fileLoaders.Add(".xml", new FrameXmlFileDocumentationLoader { ScriptReferenceHandler = OnScriptReferenced });
            fileLoaders.Add(".lua", new LuaFileDocumentationLoader());
        }

        /// <summary>
        /// Loads the documentation.
        /// </summary>
        /// <returns>
        /// An instance of the <see cref="Documentation"/> class.
        /// </returns>
        public sealed override DocumentationScope LoadDocumentation()
        {
            var globalScope = new DocumentationScope();

            foreach (var tocFile in this.GetFilesFromToc())
                LoadFileDocumentation(globalScope, tocFile);

            return globalScope;
        }

        private void OnScriptReferenced(DocumentationScope globalScope, string referencedScriptPath)
        {
            this.LoadFileDocumentation(globalScope, referencedScriptPath);
        }

        private void LoadFileDocumentation(DocumentationScope globalScope, string filePath)
        {
            var fileLoader = fileLoaders[Path.GetExtension(filePath)];
            if (fileLoader == null)
                return;

            using (var tocFileStream = streamProvider.GetFileStream(filePath))
            {
                if (tocFileStream == null)
                    return;

                var fileDocumentationScope = fileLoader.LoadFileDocumentation(tocFileStream);
                if (fileDocumentationScope != null)
                {
                    fileDocumentationScope.Name = filePath;
                    globalScope.Merge(fileDocumentationScope.GlobalScope);
                }
            }
        }

        private IEnumerable<string> GetFilesFromToc()
        {
            using (var tocStream = streamProvider.GetTocStream())
            {
                if (tocStream == null)
                    yield break;

                using (var streamReader = new StreamReader(tocStream))
                {
                    while (!streamReader.EndOfStream)
                    {
                        // Read the next line from the TOC file
                        string tocLine = streamReader.ReadLine().Trim();

                        // Make sure line is not an empty line, a comment or metadata
                        if (!String.IsNullOrEmpty(tocLine) && !tocLine.StartsWith("#"))
                            yield return tocLine;
                    }
                }
            }
        }
    }
}
